home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 299_01 / mel.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-12-28  |  41.5 KB  |  1,310 lines

  1. /*
  2. ---------------------------------------------------------------------------
  3. filename: mel.c
  4. author: g. m. crews
  5. creation date: 28-Jul-1988
  6. date of last revision: 19-Jul-1989
  7.  
  8. MEL is a comprehensive metalanguage input/output processor for engineering 
  9. analysis programs. its purposes are 1) to save the programmer development time
  10. when providing a user-friendly i/o interface, and 2) to establish a common
  11. protocol for interprogram messaging. these routines can be used by any
  12. analysis program for which a "dictionary" has been defined. see file
  13. "mel.doc" for more information.
  14.  
  15. note: these functions will normally return 0 if no error was encountered, 
  16. else a number (code) reflecting the type of error.
  17.  
  18. functions contained in this file:
  19.  
  20.     module #   name             global scope?
  21.     --------   -----------------------     -------------
  22.     1.0        meli_file         yes
  23.     1.1        end_of_data_in_file     no
  24.     1.2        meli             yes
  25.     1.2.1      compact_str         no
  26.     1.2.2      get_descrip_type      no
  27.     1.2.2.1    name_match         no
  28.     1.2.3      get_param_name         no
  29.     1.2.4      get_param_values      no
  30.     1.2.5      get_param_units         no
  31.     2.0        meli_descrip_type     yes
  32.     3.0        meli_num_params         yes
  33.     4.0        meli_param         yes
  34.     5.0        meli_data         yes
  35.     6.0        melo_init         yes
  36.     7.0        melo_data         yes
  37.     8.0        melo_file         yes
  38.     8.1        melo             yes
  39.     8.1.1      write_units_if_any     no
  40. ---------------------------------------------------------------------------
  41. */
  42.  
  43. #include <stdio.h>
  44. #include <string.h>
  45.  
  46. /*
  47. ---------------------------------------------------------------------------
  48. the following include file declares the dictionary that is to be used for
  49. MEL i/o.  note that this file will be unique for every program that uses
  50. this method of i/o.  (again, see file "mel.doc" for more information on how
  51. to further customize or modify this file.)
  52.  
  53. it also contains "public" (global) function prototyping and miscellaneous 
  54. data structures.
  55. ---------------------------------------------------------------------------
  56. */
  57.  
  58. #define MEL_INPUT
  59. #define MEL_OUTPUT
  60. #define MEL_PRIVATE
  61. #define MEL_INIT
  62. #include "mel.h"
  63.  
  64. /* "private" function prototyping:
  65.    (visible only to other routines in this file) */
  66.  
  67. static int end_of_data_in_file(int);
  68. static void compact_str(void);
  69. static int get_descrip_type(void);
  70. static int name_match(char *, char *, int);
  71. static int get_param_name(void);
  72. static int get_param_values(void);
  73. static int get_param_units(void);
  74. static void write_units_if_any(int);
  75.  
  76. /* "private" (local external) storage: */
  77.  
  78. static char descrip_str[MELI_MAX_DESCRIP_STR_LEN+1];
  79.     /* temp storage for descriptor string.
  80.        (read from file or given by MEL user). */
  81. static char *descrip_str_ptr;
  82.     /* current point of interest in above string. it is used by several 
  83.        routines that "translate" the string into MEL data structure. it points 
  84.        to the next character needing to be "translated". */
  85. static int curr_descrip_index;
  86.     /* what is descriptor's index into meli_descrip[] array? this allows
  87.        other local routines to know which parameters to check for. */
  88. static int datum_param_index;
  89.     /* where does this parameter's data go in meli_datum? */
  90.  
  91. /* the following variables are also local and deal with parameter handling: */
  92.  
  93. static int equals_sign_found_flag;
  94.     /* will there be a need to try and read a parameter value? */
  95. static int curr_param_index;
  96.     /* what is parameter's index into meli_descrip.param[] array? */
  97. static int param_name_found_flag;
  98.     /* as a safety measure, note when a parameter name has been found
  99.        for a descriptor (and do not let values without names follow). */
  100. static int left_parenthesis_found_flag;
  101.     /* will there be a need to try and read units? */
  102.  
  103. /*
  104. ---------------------------------------------------------------------------
  105. module 1.0
  106.  
  107. get a descriptor string from a file and put data into structure for easy
  108. access. that is:
  109.     read a descriptor from a file into "private" storage (descrip_str).
  110.     check for file read errors.
  111.     stop reading after semicolon encountered.
  112.     put a copy into meli_descriptor_string.
  113.     translate this string into MEL data structure (meli_datum).
  114.  
  115. algorithm synopsis: read characters from input stream until semi-
  116. colon (end-of-descriptor) is encountered, while putting them into
  117. private storage string.
  118. ---------------------------------------------------------------------------
  119. */
  120. int meli_file(
  121. FILE *input_file_handle)
  122. {
  123.     static int curr_line_num = 1;
  124.     /* assume that reading starts with the first line in a text file
  125.        and continues incrementally (one descriptor at a time). thus,
  126.        increment this variable everytime a newline character is
  127.        encountered. (that a descriptor may span several lines.) */
  128.  
  129.     int i;
  130.     /* loop counter. */
  131.  
  132.     int ch;
  133.     /* current char from input stream. */
  134.  
  135.     meli_datum.start_line = curr_line_num;
  136.     /* save the starting line number for this descriptor. */
  137.  
  138.     /* read from the input stream one character at a time: */
  139.  
  140.     for (i = 0; i < MELI_MAX_DESCRIP_STR_LEN; i++) {
  141.     ch = fgetc(input_file_handle);
  142.  
  143.     /* check for a file read error: */
  144.     if (ferror(input_file_handle)) {
  145.         mel_err.type = mel_read_err;
  146.         mel_err.start_line = meli_datum.start_line;
  147.         mel_err.end_line = curr_line_num;
  148.         strcpy(mel_err.msg, "MEL input file");
  149.         return mel_read_err;
  150.     };
  151.  
  152.     /* also error if unexpected end of file before semicolon: */
  153.     if ((ch == EOF) || (feof(input_file_handle))) {
  154.  
  155.         /* an exception is for the case of EOF right after a semicolon
  156.            (not counting any whitespace that might also have been read).
  157.            this is called the "end-of-data" error (which really isn't an
  158.            error at all, but a means of letting the caller know that no
  159.            more descriptors are in the file). */
  160.         if (end_of_data_in_file(i)) {
  161.         mel_err.type = mel_end_of_data_err;
  162.         mel_err.start_line = meli_datum.start_line;
  163.         mel_err.end_line = curr_line_num;
  164.         strcpy(mel_err.msg, "MEL input file");
  165.         return mel_end_of_data_err;
  166.         } else {
  167.         mel_err.type = mel_end_of_file_err;
  168.         mel_err.start_line = meli_datum.start_line;
  169.         mel_err.end_line = curr_line_num;
  170.         strcpy(mel_err.msg, "Missing semicolon, MEL input file");
  171.         return mel_end_of_file_err;
  172.         }
  173.     };
  174.  
  175.     if (ch == ';') break;
  176.         /* break the loop if a semicolon is encountered. */
  177.  
  178.     if (ch == '\n') curr_line_num++;
  179.         /* increment line number count for a new line */
  180.  
  181.     /* now add this character to the descriptor string and go get the
  182.        next character: */
  183.     descrip_str[i] = (char)ch;
  184.     }
  185.  
  186.     /* the descriptor has now been successfully read into the descriptor
  187.        string. time to tidy-up. */
  188.  
  189.     descrip_str[i++] = (char)ch;  /* tack on the semicolon */
  190.     descrip_str[i] = '\0';
  191.     meli_datum.end_line = curr_line_num;
  192.     strcpy(meli_descriptor_string, descrip_str);
  193.     /* make copy in case user wants to see it. */
  194.  
  195.     /* lastly, translate it (put data into meli_datum): */
  196.     return meli();
  197. }
  198.  
  199. /*
  200. ---------------------------------------------------------------------------
  201. module 1.1
  202.  
  203. has all the data been read from the MEL input file?
  204.  
  205. this routine is called when an end of file has been encountered. we must make
  206. sure that the user has not forgotten to append a semicolon on the last
  207. descriptor.  thus, it is an error condition if anything except whitespace
  208. has been read since the last semicolon (end-of-descriptor character).
  209.  
  210. return 1 if only whitespace has been read since last semicolon.
  211. return 0 if other (data) characters have been read since last semicolon.
  212. ---------------------------------------------------------------------------
  213. */
  214. static int end_of_data_in_file(
  215. int str_len)
  216. {
  217.     descrip_str[str_len] = '\0';
  218.     /* make string out of characters read since last semicolon. */
  219.  
  220.     compact_str();    /* remove whitespace from this string */
  221.  
  222.     if (strlen(descrip_str)) return 0;
  223.     else return 1;
  224.     /* if anything left over after compaction, then user has made
  225.        an error. */
  226. }
  227.  
  228. /*
  229. ---------------------------------------------------------------------------
  230. module 1.2
  231.  
  232. interpret a MEL descriptor:
  233.  
  234. this function scans a descriptor string and interprets it.
  235.  
  236. the results are put into the mel_data structure, or if an error occurs,
  237. into the mel_err structure.
  238.  
  239. algorithm synopsis: first translate what kind of descriptor this is,
  240. then loop while translating each parameter.
  241.  
  242. ---------------------------------------------------------------------------
  243. */
  244. int meli()
  245. {
  246.     int result;
  247.  
  248.     param_name_found_flag = 0;
  249.     /* allow parameter values to be input, in order, without their
  250.        names (until a parameter name is encountered).  see
  251.        function get_param_name() for more info. */
  252.  
  253.     meli_datum.num_param = 0;
  254.     /* how many parameter have been processed so far? */
  255.  
  256.     /* first place string into local storage, strip trailing semicolon,
  257.        and "compact" it: */
  258.  
  259.     strncpy(descrip_str, meli_descriptor_string,
  260.     (size_t) MELI_MAX_DESCRIP_STR_LEN);
  261.     descrip_str[MELI_MAX_DESCRIP_STR_LEN+1] = '\0';
  262.     /* ensure no overflow. */
  263.  
  264.     compact_str();
  265.  
  266.     /* next keep evaluating until an error is encountered or the end of
  267.        the string is reached (functions return 0 if no err): */
  268.     
  269.     if (result = get_descrip_type()) return result;
  270.     /* every descriptor must have a type. */
  271.  
  272.     /* there may be a variable number of parameters and values: */
  273.  
  274.     while (*descrip_str_ptr != '\0') {
  275.     /* keep going until end of descrip_str reached */
  276.  
  277.     datum_param_index = meli_datum.num_param;
  278.         /* where will new parameter data go? */
  279.  
  280.     /* a parameter usually consists of its name, value, and units: */
  281.     if (result = get_param_name()) return result;
  282.     if (result = get_param_values()) return result;
  283.     if (result = get_param_units()) return result;
  284.  
  285.     meli_datum.num_param++;
  286.         /* another parameter has been processed! */
  287.     };
  288.  
  289.     return 0;
  290.     /* the end of the descriptor string has been reached. */
  291. }
  292.  
  293. /*
  294. ---------------------------------------------------------------------------
  295. module 1.2.1
  296.  
  297. compact the descrip_str string:
  298.     flush whitespace except blanks within single quotes or curly brackets
  299.     (strings and arrays).
  300.     flush comments (text within double quotes).
  301.  
  302. algorithm synopsis:  since the string can only shrink while being compacted,
  303. use pointers to keep track of current positions and overwrite in place.
  304. ---------------------------------------------------------------------------
  305. */
  306. static void compact_str()
  307. {
  308.     char *p;    /* pointer to current end of compacted string */
  309.     char *q;    /* pointer to current end of uncompacted string */
  310.  
  311.     int single_quote_flag = 0;
  312.     int curly_brackets_flag = 0;
  313.     /* all blanks within a descriptor are flushed except those within
  314.        single quotes or curly brackets. */
  315.  
  316.     int double_quote_flag = 0;
  317.     /* all text within double quotes are comments and are flushed. */
  318.  
  319.     p = q = descrip_str;    /* start at the beginning */
  320.  
  321.     do { /* look at current character, does it need to be flushed? */
  322.  
  323.     /* if the character is a double quote, flush all chars between it
  324.        and the next double quote: */
  325.     if (*q == '"') {
  326.         double_quote_flag = !double_quote_flag;
  327.         q++;     /* has effect of flushing this character (typical) */
  328.         continue;   /* go to next character (typical) */
  329.     }
  330.  
  331.     /* if the character is a newline, flush it */
  332.     else if (*q == '\n') {
  333.         q++;
  334.         continue;
  335.     }
  336.  
  337.     /* flush all chars if we are currently between double quotes: */
  338.     else if (double_quote_flag) {
  339.         q++;
  340.         continue;
  341.     }
  342.  
  343.     /* test for single quote or curly brackets and adjust flag value: */
  344.     else if (*q == '\'') single_quote_flag = !single_quote_flag;
  345.     else if (*q == '{') curly_brackets_flag = 1;
  346.     else if (*q == '}') curly_brackets_flag = 0;
  347.  
  348.     /* if the character is some other whitespace char, except a blank,
  349.        also flush it: */
  350.     else if ((*q == '\t') || (*q == '\v') || (*q == '\r') ||
  351.     (*q == '\f')) {
  352.         q++;
  353.         continue;
  354.     }
  355.  
  356.     /* if the character is a blank, flush it unless it is between
  357.        single quotes or curly brackets: */
  358.     else if ((*q == ' ') && !single_quote_flag && !curly_brackets_flag) {
  359.         q++;
  360.         continue;
  361.     }
  362.  
  363.     /* this character not to be flushed, so copy it in place */
  364.     *p = *q;
  365.     p++; q++;   /* get ready for next character */
  366.  
  367.     } while (*(q-1) != '\0');
  368.     /* don't continue if last char was null terminator */
  369. }
  370.  
  371. /*
  372. ---------------------------------------------------------------------------
  373. module 1.2.2
  374.  
  375. determine current descriptor's index into meli_descrip array.
  376. that is, which type of descriptor has just been read?
  377. ---------------------------------------------------------------------------
  378. */
  379. static int get_descrip_type()
  380. {
  381.  
  382.     int i;
  383.     /* loop counter. */
  384.  
  385.     char temp_str[MELI_MAX_DESCRIP_STR_LEN+1];
  386.     /* build up descriptor name here. */
  387.  
  388.     descrip_str_ptr = descrip_str;
  389.     /* must reset current string position to start of string since
  390.        now time to start another descriptor. */
  391.  
  392.     /* create string to next comma or end of descrip string: */
  393.     for (i = 0; i < MELI_MAX_DESCRIP_STR_LEN; i++) {
  394.     if ((*descrip_str_ptr == ',') ||
  395.         (*descrip_str_ptr == ';') ||
  396.         (*descrip_str_ptr == '\0')) {
  397.         descrip_str_ptr++;      /* get ready for next part */
  398.         temp_str[i] = '\0';
  399.         break;
  400.     };
  401.  
  402.     temp_str[i] = *descrip_str_ptr++;
  403.     /* add this char to temp string. */
  404.     }
  405.  
  406.     /* find descrip name that matches this built up name: */
  407.  
  408.     for (i = 0; i < MELI_NUM_DESCRIP_NAMES; i++)
  409.     if (name_match(meli_descrip[i].name, temp_str,
  410.         meli_descrip[i].min_name_len)) {
  411.         curr_descrip_index = meli_datum.descrip_type = i;
  412.         return 0;     /* descriptor type has been found */
  413.     };
  414.  
  415.     /* error if went through entire list without finding match: */
  416.  
  417.     mel_err.type = mel_unknown_descrip_name_err;
  418.     mel_err.start_line = meli_datum.start_line;
  419.     mel_err.end_line = meli_datum.end_line;
  420.     strncpy(mel_err.msg, temp_str, MEL_MAX_ERR_MSG_LEN);
  421.     mel_err.msg[MEL_MAX_ERR_MSG_LEN+1] = '\0';
  422.     return mel_unknown_descrip_name_err;
  423. }
  424.  
  425. /*
  426. ---------------------------------------------------------------------------
  427. module 1.2.2.1
  428.  
  429. determine if the starting characters in a test string start off with all
  430. the characters in a reference string. returns: 0 = no, 1 = yes.
  431. ---------------------------------------------------------------------------
  432. */
  433. static int name_match(
  434. char test_str[],
  435. char ref_str[],
  436. int ref_str_len)
  437. {
  438.     int i;
  439.  
  440.     for (i = 0; i < ref_str_len; i++)
  441.     if (ref_str[i] != test_str[i]) return 0;
  442.  
  443.     return 1;
  444. }
  445.  
  446. /*
  447. ---------------------------------------------------------------------------
  448. module 1.2.3
  449.  
  450. parameters usually are part of a descriptor.  get the next parameter name,
  451. if any, and find out which parameter it is.  note that MEL also allows
  452. parameter values to be input, without their names, assuming a certain order.
  453.  
  454. algorithm synopsis: copy next parameter starting at current position
  455. in descrip_str.  if unrecognizable, it may be a value (parameter name
  456. assumed) so allow for this possibility.
  457.  
  458. ---------------------------------------------------------------------------
  459. */
  460. static int get_param_name()
  461. {
  462.     int i;
  463.     char temp_str[MELI_MAX_DESCRIP_STR_LEN+1];
  464.     char *old_descrip_str_ptr;
  465.  
  466.     /* see if the maximum number of descriptors have already been read: */
  467.     /* (must be able to read another or at end of descriptor string.) */
  468.     /* this test prevents too many parameters from being read. */
  469.  
  470.     if ((meli_datum.num_param >= meli_descrip[meli_datum.descrip_type].
  471.     max_num_param) && (*descrip_str_ptr != '\0')) {
  472.     mel_err.type = mel_too_many_param_err;
  473.     mel_err.start_line = meli_datum.start_line;
  474.     mel_err.end_line = meli_datum.end_line;
  475.     strcpy(mel_err.msg, descrip_str);
  476.     return mel_too_many_param_err;
  477.     };
  478.  
  479.     /* create string to next comma, equal's sign, semicolon, or null terminator.
  480.        however, since this may be a value and not a parameter name, remember
  481.        the old string posn just in case: */
  482.  
  483.     old_descrip_str_ptr = descrip_str_ptr;
  484.  
  485.     for (i = 0; i < MELI_MAX_DESCRIP_STR_LEN; i++) {
  486.     if ((*descrip_str_ptr == ',') ||
  487.         (*descrip_str_ptr == ';') ||
  488.         (*descrip_str_ptr == '\0')) {
  489.         equals_sign_found_flag = 0;
  490.         descrip_str_ptr++;
  491.         temp_str[i] = '\0';
  492.         break;
  493.     } else if (*descrip_str_ptr == '=') {
  494.         equals_sign_found_flag = 1;
  495.         descrip_str_ptr++;
  496.         temp_str[i] = '\0';
  497.         break;
  498.     };
  499.  
  500.     temp_str[i] = *descrip_str_ptr++;
  501.     }
  502.  
  503.     /* find the parameter name that matches this built up name (assuming
  504.        it is a name): */
  505.  
  506.     for (i = 0; i < meli_descrip[curr_descrip_index].max_num_param; i++)
  507.     if (name_match(meli_descrip[curr_descrip_index].param[i].name,
  508.         temp_str, meli_descrip[curr_descrip_index].param[i].min_name_len)) {
  509.         curr_param_index = i;
  510.         param_name_found_flag = 1;
  511.         meli_datum.param[datum_param_index].name_index = i;
  512.         return 0;   /* the parameter's name has been found! */
  513.     };
  514.  
  515.     /* if we get here then we went through the entire list without finding
  516.        a match.  however, this may not be an error for the case of
  517.        parameter values being input in order (without their names): */
  518.  
  519.     /* if an equal's sign was found, then this is definitely an error: */
  520.     if (equals_sign_found_flag) {
  521.     mel_err.type = mel_unknown_param_name_err;
  522.     mel_err.start_line = meli_datum.start_line;
  523.     mel_err.end_line = meli_datum.end_line;
  524.     strcpy(mel_err.msg, "Or misplaced equal's sign");
  525.     return mel_unknown_param_name_err;
  526.     };
  527.  
  528.     /* in order to avoid confusion, once a parameter has been found, make it
  529.        no longer legal to enter just parameter values alone: */
  530.     if (param_name_found_flag) {
  531.     mel_err.type = mel_missing_param_name_err;
  532.     mel_err.start_line = meli_datum.start_line;
  533.     mel_err.end_line = meli_datum.end_line;
  534.     strcpy(mel_err.msg, "Or unrecognizable parameter name");
  535.     return mel_missing_param_name_err;
  536.     };
  537.  
  538.     /* prepare for the next routine to read the next parameter value in
  539.        order: */
  540.  
  541.     curr_param_index = datum_param_index;
  542.     meli_datum.param[datum_param_index].name_index = curr_param_index;
  543.     equals_sign_found_flag = 1;   /* fake it */
  544.     descrip_str_ptr = old_descrip_str_ptr;
  545.     /* restore pointer so that value may be read by next routine. */
  546.     return 0;
  547. }
  548.  
  549. /*
  550. ---------------------------------------------------------------------------
  551. module 1.2.4
  552.  
  553. a parameter's value follows a parameter's name.  read the value(s), if any,
  554. and store them.
  555. ---------------------------------------------------------------------------
  556. */
  557. static int get_param_values()
  558. {
  559.     int i;
  560.     char temp_str[MELI_MAX_DESCRIP_STR_LEN+1];
  561.     enum mel_data_types data_type;
  562.     int result;
  563.     int curr_temp_posn;
  564.     int curr_array_index = 0;
  565.     int end_of_temp_str;
  566.     int single_quote_found_flag = 0;
  567.  
  568.     /* create string to next comma, semicolon, or null terminator:
  569.        (an exception is to allow a comma or semicolon within a single-
  570.        quoted substring.) note: string may include array of values in
  571.        {brackets}. */
  572.  
  573.     for (i = 0; i < MELI_MAX_DESCRIP_STR_LEN; i++) {
  574.     if (*descrip_str_ptr == '\'')
  575.         single_quote_found_flag = !single_quote_found_flag;
  576.  
  577.     if (single_quote_found_flag) {
  578.         if (*descrip_str_ptr == '\0') {
  579.         left_parenthesis_found_flag = 0;
  580.         descrip_str_ptr++;
  581.         temp_str[i] = '\0';
  582.         break;
  583.         };
  584.     } else if ((*descrip_str_ptr == ',') ||
  585.         (*descrip_str_ptr == ';') ||
  586.         (*descrip_str_ptr == '\0')) {
  587.         left_parenthesis_found_flag = 0;
  588.         descrip_str_ptr++;
  589.         temp_str[i] = '\0';
  590.         break;
  591.     } else if (*descrip_str_ptr == '(') {
  592.         left_parenthesis_found_flag = 1;
  593.         descrip_str_ptr++;
  594.         temp_str[i] = '\0';
  595.         break;
  596.     };
  597.  
  598.     temp_str[i] = *descrip_str_ptr++;
  599.     }
  600.  
  601.     /* first see if the value of the parameter is "unknown": */
  602.     if (!strcmp(temp_str, "unknown")) {
  603.     meli_datum.param[datum_param_index].unknown_flag = 1;
  604.     return 0;
  605.     } else meli_datum.param[datum_param_index].unknown_flag = 0;
  606.  
  607.     /* scan the string and interpret its value(s) (depending on which kind of
  608.        parameter this value belongs to): */
  609.  
  610.     data_type = meli_descrip[curr_descrip_index].param[curr_param_index].type;
  611.  
  612.     switch (data_type) {
  613.  
  614.     case (mel_bool):   /* read true or false (default is true) */
  615.         if ((temp_str[0] == '\0') || (temp_str[0] != 'f'))
  616.         meli_datum.param[datum_param_index].data.integer = 1;
  617.         else
  618.         meli_datum.param[datum_param_index].data.integer = 0;
  619.  
  620.         result = 1;  /* successful "reading" of value */
  621.         break;
  622.  
  623.     case (mel_int):    /* read an integer */
  624.         result = sscanf(temp_str, "%d",
  625.         &meli_datum.param[datum_param_index].data.integer);
  626.         break;
  627.  
  628.     case (mel_real):      /* read a real */
  629.         result = sscanf(temp_str, "%lf",
  630.         &meli_datum.param[datum_param_index].data.real);
  631.         break;
  632.  
  633.     case (mel_str):    /* read a string (may be surrounded by single 
  634.                   quotes*/
  635.         /* string MUST have single quotes if has blanks: */
  636.         if (temp_str[0] == '\'')
  637.         result = sscanf(&temp_str[1], "%[^']",
  638.             meli_datum.param[datum_param_index].data.string);
  639.         else
  640.         result = sscanf(temp_str, "%s",
  641.             meli_datum.param[datum_param_index].data.string);
  642.         break;
  643.  
  644.     case (mel_int_array):
  645.  
  646.         /* get ready to read numbers in array: */
  647.         end_of_temp_str = strlen(temp_str);
  648.  
  649.         /* first make sure first and last characters in string were
  650.            curly brackets and adjust string length to get rid of them: */
  651.         if ((temp_str[0] != '{') || (temp_str[end_of_temp_str-1] != '}')) {
  652.         mel_err.type = mel_missing_bracket_err;
  653.         mel_err.start_line = meli_datum.start_line;
  654.         mel_err.end_line = meli_datum.end_line;
  655.         strncpy(mel_err.msg, temp_str, MEL_MAX_ERR_MSG_LEN);
  656.         mel_err.msg[MEL_MAX_ERR_MSG_LEN+1] = '\0';
  657.         return mel_missing_bracket_err;
  658.         };
  659.         temp_str[--end_of_temp_str] = '\0';  /* last char was bracket */
  660.         while (temp_str[end_of_temp_str - 1] == ' ')
  661.         temp_str[--end_of_temp_str] = '\0';  /* flush trailing blanks */
  662.         curr_temp_posn = 1;  /* first char was curly bracket */
  663.  
  664.         /* now read in values: */
  665.         result = 1;      /* assume a good start */
  666.  
  667.         while ((curr_temp_posn < end_of_temp_str) &&
  668.         (result != 0) && (result != EOF)) {
  669.  
  670.         /* be careful not to overflow array: */
  671.         if (curr_array_index > MELI_MAX_PARAM_INT_ARRAY_LEN)
  672.             break;
  673.  
  674.         /* purge till first non-blank character: */
  675.         for (; curr_temp_posn < end_of_temp_str; curr_temp_posn++)
  676.             if (temp_str[curr_temp_posn] != ' ') break;
  677.  
  678.         /* read next integer into array: */
  679.         result = sscanf(&temp_str[curr_temp_posn], "%d",
  680.             &meli_datum.param[datum_param_index].
  681.             data.integer_array[curr_array_index++]);
  682.  
  683.         /* purge till first blank character: */
  684.         for (; curr_temp_posn < end_of_temp_str; curr_temp_posn++)
  685.             if (temp_str[curr_temp_posn] == ' ') break;
  686.         };
  687.  
  688.         break;
  689.  
  690.     case (mel_real_array):
  691.         /* get ready to read numbers in array: */
  692.         end_of_temp_str = strlen(temp_str);
  693.  
  694.         /* first make sure first and last characters in string were
  695.            curly brackets and adjust string length to get rid of them: */
  696.         if ((temp_str[0] != '{') || (temp_str[end_of_temp_str-1] != '}')) {
  697.         mel_err.type = mel_missing_bracket_err;
  698.         mel_err.start_line = meli_datum.start_line;
  699.         mel_err.end_line = meli_datum.end_line;
  700.         strncpy(mel_err.msg, temp_str, MEL_MAX_ERR_MSG_LEN);
  701.         mel_err.msg[MEL_MAX_ERR_MSG_LEN+1] = '\0';
  702.         return mel_missing_bracket_err;
  703.         };
  704.         temp_str[--end_of_temp_str] = '\0';  /* last char was bracket */
  705.         while (temp_str[end_of_temp_str - 1] == ' ')
  706.         temp_str[--end_of_temp_str] = '\0';  /* flush trailing blanks */
  707.         curr_temp_posn = 1;  /* first char is curly bracket */
  708.         result = 1;
  709.  
  710.         while ((curr_temp_posn < end_of_temp_str) &&
  711.         (result != 0) && (result != EOF)) {
  712.  
  713.         /* be careful not to overflow array: */
  714.         if (curr_array_index > MELI_MAX_PARAM_REAL_ARRAY_LEN)
  715.             break;
  716.  
  717.         /* purge till first non-blank character: */
  718.         for (; curr_temp_posn < end_of_temp_str; curr_temp_posn++)
  719.             if (temp_str[curr_temp_posn] != ' ') break;
  720.  
  721.         /* read next real into array: */
  722.         result = sscanf(&temp_str[curr_temp_posn], "%lf",
  723.             &meli_datum.param[datum_param_index].
  724.             data.real_array[curr_array_index++]);
  725.  
  726.         /* purge till first blank character: */
  727.         for (; curr_temp_posn < end_of_temp_str; curr_temp_posn++)
  728.             if (temp_str[curr_temp_posn] == ' ') break;
  729.         };
  730.  
  731.         break;
  732.  
  733.     case (mel_str_array):
  734.         /* get ready to read string into array: */
  735.         end_of_temp_str = strlen(temp_str);
  736.  
  737.         /* first make sure first and last characters in string were
  738.            curly brackets and adjust string length to get rid of them: */
  739.         if ((temp_str[0] != '{') || (temp_str[end_of_temp_str-1] != '}')) {
  740.         mel_err.type = mel_missing_bracket_err;
  741.         mel_err.start_line = meli_datum.start_line;
  742.         mel_err.end_line = meli_datum.end_line;
  743.         strncpy(mel_err.msg, temp_str, MEL_MAX_ERR_MSG_LEN);
  744.         mel_err.msg[MEL_MAX_ERR_MSG_LEN+1] = '\0';
  745.         return mel_missing_bracket_err;
  746.         };
  747.         temp_str[--end_of_temp_str] = '\0';  /* last char was bracket */
  748.         while (temp_str[end_of_temp_str - 1] == ' ')
  749.         temp_str[--end_of_temp_str] = '\0';  /* flush trailing blanks */
  750.         curr_temp_posn = 1;  /* first char is curly bracket */
  751.         result = 1;
  752.  
  753.         while ((curr_temp_posn < end_of_temp_str) &&
  754.         (result != 0) && (result != EOF)) {
  755.  
  756.         /* be careful not to overflow array: */
  757.         if (curr_array_index > MELI_MAX_PARAM_STR_ARRAY_LEN)
  758.             break;
  759.  
  760.         /* purge till first non-blank character: */
  761.         for (; curr_temp_posn < end_of_temp_str; curr_temp_posn++)
  762.             if (temp_str[curr_temp_posn] != ' ') break;
  763.  
  764.         /* read next real into array (remember 1st char may be
  765.            single quote, so two possibilites to consider): */
  766.         if (temp_str[curr_temp_posn] == '\'') {
  767.  
  768.             result = sscanf(&temp_str[++curr_temp_posn], "%[^']",
  769.             meli_datum.param[datum_param_index].
  770.             data.string_array[curr_array_index++]);
  771.  
  772.             /* purge till first char after next single quote: */
  773.             for (; curr_temp_posn < end_of_temp_str; curr_temp_posn++)
  774.             if (temp_str[curr_temp_posn] == '\'') break;
  775.             curr_temp_posn++;
  776.  
  777.         } else {
  778.  
  779.             result = sscanf(&temp_str[curr_temp_posn], "%s",
  780.             meli_datum.param[datum_param_index].
  781.             data.string_array[curr_array_index++]);
  782.  
  783.             /* purge till next blank character: */
  784.             for (; curr_temp_posn < end_of_temp_str; curr_temp_posn++)
  785.             if (temp_str[curr_temp_posn] == ' ') break;
  786.         }
  787.         };
  788.  
  789.         break;
  790.     }
  791.  
  792.     meli_datum.param[datum_param_index].array_len = curr_array_index;
  793.  
  794.     /* check to see if read was successful: */
  795.     if ((result == 0) || (result == EOF)) {
  796.     mel_err.type = mel_param_data_err;
  797.     mel_err.start_line = meli_datum.start_line;
  798.     mel_err.end_line = meli_datum.end_line;
  799.     strncpy(mel_err.msg, temp_str, MEL_MAX_ERR_MSG_LEN);
  800.     mel_err.msg[MEL_MAX_ERR_MSG_LEN+1] = '\0';
  801.     return mel_param_data_err;
  802.     };
  803.  
  804.     return 0;
  805. }
  806.  
  807. /*
  808. ---------------------------------------------------------------------------
  809. module 1.2.5
  810.  
  811. sometimes the units associated with parameter data may change:
  812. ---------------------------------------------------------------------------
  813. */
  814. static int get_param_units()
  815. {
  816.     int i;
  817.     char temp_str[MELI_MAX_DESCRIP_STR_LEN+1];
  818.  
  819.     /* are there any units to get? */
  820.     /* if not then return null string for units. */
  821.     if (!left_parenthesis_found_flag) {
  822.     strcpy(meli_datum.param[datum_param_index].units, "");
  823.     return 0;
  824.     }
  825.  
  826.     /* create temporary string containing units: */
  827.     for (i = 0; i < MELI_MAX_DESCRIP_STR_LEN; i++) {
  828.     if (*descrip_str_ptr == ')') {
  829.         descrip_str_ptr++;
  830.         if (*descrip_str_ptr == ',' ||
  831.         *descrip_str_ptr == ';') descrip_str_ptr++;
  832.         /* normally, a comma or semicolon should follow the units.
  833.            lets be forgiving in case there isn't and not just flush
  834.            anything. */
  835.         temp_str[i] = '\0';
  836.         break;
  837.     } else if (*descrip_str_ptr == '\0') {
  838.         mel_err.type = mel_missing_paren_err;
  839.         mel_err.start_line = meli_datum.start_line;
  840.         mel_err.end_line = meli_datum.end_line;
  841.         temp_str[i] = '\0';
  842.         strncpy(mel_err.msg, temp_str, MEL_MAX_ERR_MSG_LEN);
  843.         mel_err.msg[MEL_MAX_ERR_MSG_LEN+1] = '\0';
  844.         return mel_missing_paren_err;
  845.     };
  846.     temp_str[i] = *descrip_str_ptr++;
  847.     }
  848.  
  849.     /* copy this string to units area: */
  850.     temp_str[MELO_UNITS_STR_LEN+1] = '\0';
  851.     strcpy(meli_datum.param[datum_param_index].units, temp_str);
  852.     return 0;
  853. }
  854.  
  855. /*
  856. ---------------------------------------------------------------------------
  857. module 2.0
  858.  
  859. extract the type of descriptor that has been input.
  860. see mel.doc for more information on using this procedure.
  861. ---------------------------------------------------------------------------
  862. */
  863. char *meli_descrip_type()
  864. {
  865.     return meli_descrip[meli_datum.descrip_type].name;
  866. }
  867.  
  868. /*
  869. ---------------------------------------------------------------------------
  870. module 3.0
  871.  
  872. extract the number of parameters that has been input.
  873. see mel.doc for more information on using this procedure.
  874. ---------------------------------------------------------------------------
  875. */
  876. int meli_num_params()
  877. {
  878.     return meli_datum.num_param;
  879. }
  880.  
  881. /*
  882. ---------------------------------------------------------------------------
  883. module 4.0
  884.  
  885. extract information associated with the param_num'th parameter that has
  886. been input.
  887. ---------------------------------------------------------------------------
  888. */
  889. int meli_param(
  890. int param_num,              /* which parameter are we interested in? */
  891. char *param,              /* data associated with this param: */
  892. union meli_param_data *data,
  893. char *units,
  894. int *array_len,
  895. int *unknown_flag)
  896. {
  897.  
  898.     /* is param_num within range of number of params input? */
  899.     if (param_num >= 0 && param_num < meli_datum.num_param) {
  900.         /* yes it is! */
  901.         strcpy(param, meli_descrip[meli_datum.descrip_type].
  902.         param[meli_datum.param[param_num].name_index].name);
  903.         *data = meli_datum.param[param_num].data;
  904.         strcpy(units, meli_datum.param[param_num].units);
  905.         *array_len = meli_datum.param[param_num].array_len;
  906.         *unknown_flag = meli_datum.param[param_num].unknown_flag;
  907.         return 0;    /* no errors (match found) */
  908.     } else
  909.         /* index out of bounds, return an error condition: */
  910.         return 1;
  911. }
  912.  
  913. /*
  914. ---------------------------------------------------------------------------
  915. module 5.0
  916.  
  917. extract a particular parameter's value that has been input.
  918. see mel.doc for more information on using this procedure.
  919.  
  920. algorithm synopsis: see if param string was one of the parameters input with
  921. current descriptor.  if it is, then return with all the data about this
  922. parameter that was input.
  923. ---------------------------------------------------------------------------
  924. */
  925. int meli_data(
  926. char *param,              /* which parameter are we interested in? */
  927. union meli_param_data *data,  /* return with data about this parameter: */
  928. char *units,
  929. int *array_len,
  930. int *unknown_flag)
  931. {
  932.     int i;    /* loop counter */
  933.  
  934.     /* compare param with each parameter that was input: */
  935.     for (i = 0; i < meli_datum.num_param; i++)
  936.  
  937.     /* does it match? (if not, continue looking.) */
  938.     if (name_match(param, meli_descrip[meli_datum.descrip_type].
  939.         param[meli_datum.param[i].name_index].name, meli_descrip[
  940.         meli_datum.descrip_type].param[meli_datum.param[i].name_index].
  941.         min_name_len)) {
  942.  
  943.         /* yes it does! */
  944.         *data = meli_datum.param[i].data;
  945.         strcpy(units, meli_datum.param[i].units);
  946.         *array_len = meli_datum.param[i].array_len;
  947.         *unknown_flag = meli_datum.param[i].unknown_flag;
  948.         return 0;    /* no errors (match found) */
  949.     };
  950.  
  951.     /* no match was ever found, return an error condition: */
  952.     return 1;
  953. }
  954.  
  955. /*
  956. ---------------------------------------------------------------------------
  957. module 6.0
  958.  
  959. initialize melo_datum for a descriptor of type *descrip_type.
  960. ---------------------------------------------------------------------------
  961. */
  962. int melo_init(
  963. char *descrip_type)
  964. {
  965.     int i;    /* loop counter */
  966.  
  967.     /* look for descriptor of given type: */
  968.     for (i = 0; i < MELO_NUM_DESCRIP_NAMES; i++)
  969.  
  970.     /* does it match with any item in list of descriptor names? */
  971.     if (!strcmp(descrip_type, melo_descrip[i].name)) {
  972.  
  973.         /* yes, it does! */
  974.         melo_datum.descrip_type = i;
  975.         melo_datum.num_param = 0;
  976.         return 0;     /* no errors */
  977.     };
  978.  
  979.     /* no match was ever found, return an error condition: */
  980.     return 1;
  981. }
  982.  
  983. /*
  984. ---------------------------------------------------------------------------
  985. module 7.0
  986.  
  987. for parameter *param, put data in rest of arguement list into melo_datum.
  988. ---------------------------------------------------------------------------
  989. */
  990. int melo_data(
  991. char *param,              /* which parameter are we interested in? */
  992. union melo_param_data *data,  /* data about this parameter: */
  993. char units[],
  994. int array_len,
  995. int unknown_flag)
  996. {
  997.     int i;    /* loop counter */
  998.  
  999.     /* compare param with each parameter type: */
  1000.     for (i = 0; i < MELO_MAX_PARAMS; i++)
  1001.  
  1002.     /* does it match? (if not, continue looking.) */
  1003.     if (name_match(param, melo_descrip[melo_datum.descrip_type].
  1004.         param[i].name, melo_descrip[melo_datum.descrip_type].
  1005.         param[i].min_name_len)) {
  1006.  
  1007.         /* yes it does! put it in the proper place: */
  1008.         melo_datum.param[melo_datum.num_param].name_index = i;
  1009.         melo_datum.param[melo_datum.num_param].data = *data;
  1010.         strcpy(melo_datum.param[melo_datum.num_param].units, units);
  1011.         melo_datum.param[melo_datum.num_param].array_len = array_len;
  1012.         melo_datum.param[melo_datum.num_param].unknown_flag = unknown_flag;
  1013.         melo_datum.num_param++;
  1014.         return 0;    /* no errors (match found) */
  1015.     };
  1016.  
  1017.     /* no match was ever found, return an error condition: */
  1018.     return 1;
  1019. }
  1020.  
  1021. /*
  1022. ---------------------------------------------------------------------------
  1023. module 8.0
  1024.  
  1025. write MEL data to the output file.
  1026. ---------------------------------------------------------------------------
  1027. */
  1028. int melo_file(
  1029. FILE *output_file_handle,
  1030. int verbose_flag)
  1031. {
  1032.     mel_err.type = mel_no_err;    /* assume no error will occur */
  1033.  
  1034.     melo(verbose_flag);  /* translate data structure to string */
  1035.  
  1036.     /* now output the string to file: */
  1037.  
  1038.     fprintf(output_file_handle, "%s", descrip_str);
  1039.     if (ferror(output_file_handle)) {
  1040.     mel_err.type = mel_write_err;
  1041.     strcpy(mel_err.msg, "MEL output file");
  1042.     return mel_write_err;
  1043.     };
  1044.  
  1045.     return 0;
  1046.  
  1047. }
  1048.  
  1049. /*
  1050. ---------------------------------------------------------------------------
  1051. module 8.1
  1052.  
  1053. write MEL data to the descriptor string. that is, using the output dictionary,
  1054. translate data from the melo_datum data structure into a string of data. see
  1055. the file mel.doc for more information.
  1056. ---------------------------------------------------------------------------
  1057. */
  1058. void melo(
  1059. int verbose_flag)
  1060. {
  1061.     int i, j;   /* loop counters */
  1062.     enum mel_data_types type;   /* temp storage */
  1063.     char temp_str[MELO_MAX_DESCRIP_STR_LEN+1];    /* temp storage */
  1064.  
  1065.     mel_err.type = mel_no_err;    /* assume no error will occur */
  1066.  
  1067.     /* will the "output" string be verbose (human readable) or terse 
  1068.        (compressed for machine efficiency/readability)? */
  1069.  
  1070.     if (verbose_flag) {
  1071.     /* first write descriptor type: */
  1072.     strcpy(descrip_str, melo_descrip[melo_datum.descrip_type].name);
  1073.  
  1074.     /* next, write its parameters and parameter values: */
  1075.     for (i = 0; i < melo_datum.num_param; i++) {
  1076.  
  1077.         /* which parameter is it? */
  1078.         sprintf(temp_str, ",\n    %s =",
  1079.         melo_descrip[melo_datum.descrip_type].
  1080.         param[melo_datum.param[i].name_index].name);
  1081.         strcat(descrip_str, temp_str);
  1082.  
  1083.         /* is its value known? (if not, write "unknown" and
  1084.            proceed to next parameter.) */
  1085.         if (melo_datum.param[i].unknown_flag) {
  1086.         strcat(descrip_str, " unknown");
  1087.         continue;
  1088.         };
  1089.  
  1090.         /* what type of value is it (int, array, etc.)? */
  1091.  
  1092.         type = melo_descrip[melo_datum.descrip_type].
  1093.         param[melo_datum.param[i].name_index].type;
  1094.  
  1095.         switch (type) {
  1096.  
  1097.         case (mel_bool):
  1098.  
  1099.             if (melo_datum.param[i].data.integer)
  1100.             strcat(descrip_str, " true");
  1101.             else
  1102.             strcat(descrip_str, " false");
  1103.             break;
  1104.  
  1105.         case (mel_int):
  1106.  
  1107.             sprintf(temp_str, " %d", melo_datum.param[i].data.integer);
  1108.             strcat(descrip_str, temp_str);
  1109.             write_units_if_any(i);
  1110.             break;
  1111.  
  1112.         case (mel_real):
  1113.  
  1114.             sprintf(temp_str, " %lg", melo_datum.param[i].data.real);
  1115.             strcat(descrip_str, temp_str);
  1116.             write_units_if_any(i);
  1117.             break;
  1118.  
  1119.         case (mel_str):
  1120.  
  1121.             sprintf(temp_str, " '%s'", melo_datum.param[i].data.string);
  1122.             strcat(descrip_str, temp_str);
  1123.             write_units_if_any(i);
  1124.             break;
  1125.  
  1126.         case(mel_int_array):
  1127.  
  1128.             strcat(descrip_str, "{");
  1129.  
  1130.             for (j = 0; j < melo_datum.param[i].array_len; j++) {
  1131.             if (j != 0) strcat(descrip_str, " ");
  1132.             if (!(j % 5)) strcat(descrip_str, "\n        ");
  1133.             sprintf(temp_str, "%d",
  1134.                 melo_datum.param[i].data.integer_array[j]);
  1135.             strcat(descrip_str, temp_str);
  1136.             }
  1137.  
  1138.             strcat(descrip_str, "\n    }");
  1139.             write_units_if_any(i);
  1140.             break;
  1141.  
  1142.         case(mel_real_array):
  1143.  
  1144.             strcat(descrip_str, "{");
  1145.  
  1146.             for (j = 0; j < melo_datum.param[i].array_len; j++) {
  1147.             if (j != 0) strcat(descrip_str, " ");
  1148.             if (!(j % 3)) strcat(descrip_str, "\n        ");
  1149.             sprintf(temp_str, "%lg",
  1150.                 melo_datum.param[i].data.real_array[j]);
  1151.             strcat(descrip_str, temp_str);
  1152.             }
  1153.  
  1154.             strcat(descrip_str, "\n    }");
  1155.             write_units_if_any(i);
  1156.             break;
  1157.  
  1158.         case(mel_str_array):
  1159.  
  1160.             strcat(descrip_str, "{");
  1161.  
  1162.             for (j = 0; j < melo_datum.param[i].array_len; j++) {
  1163.             strcat(descrip_str, "\n        ");
  1164.             sprintf(temp_str, "'%s'",
  1165.                 melo_datum.param[i].data.string_array[j]);
  1166.             strcat(descrip_str, temp_str);
  1167.             }
  1168.  
  1169.             strcat(descrip_str, "\n    }");
  1170.             write_units_if_any(i);
  1171.             break;
  1172.         }
  1173.     }
  1174.  
  1175.     strcat(descrip_str, ";\n");
  1176.  
  1177.     } else {   /* terse output */
  1178.  
  1179.     /* first write descriptor type: */
  1180.     strncpy(descrip_str, melo_descrip[melo_datum.descrip_type].name,
  1181.         melo_descrip[melo_datum.descrip_type].min_name_len);
  1182.     descrip_str[melo_descrip[melo_datum.descrip_type].min_name_len] = '\0';
  1183.  
  1184.     /* next, write its parameters and parameter values: */
  1185.     for (i = 0; i < melo_datum.num_param; i++) {
  1186.  
  1187.         /* which parameter is it? */
  1188.         strcat(descrip_str,",");
  1189.         strncpy(temp_str, melo_descrip[melo_datum.descrip_type].
  1190.         param[melo_datum.param[i].name_index].name, melo_descrip[
  1191.         melo_datum.descrip_type].param[melo_datum.param[i].
  1192.         name_index].min_name_len);
  1193.         temp_str[melo_descrip[melo_datum.descrip_type].param[melo_datum.
  1194.         param[i].name_index].min_name_len] = '\0';
  1195.         strcat(descrip_str, temp_str);
  1196.         strcat(descrip_str, "=");
  1197.  
  1198.         /* is its value known? (if not, write "unknown" and
  1199.            proceed to next parameter.) */
  1200.         if (melo_datum.param[i].unknown_flag) {
  1201.         strcat(descrip_str, "unknown");
  1202.         continue;
  1203.         };
  1204.  
  1205.         /* what type of value is it (int, array, etc.)? */
  1206.  
  1207.         type = melo_descrip[melo_datum.descrip_type].
  1208.         param[melo_datum.param[i].name_index].type;
  1209.  
  1210.         switch (type) {
  1211.  
  1212.         case (mel_bool):
  1213.  
  1214.             if (melo_datum.param[i].data.integer)
  1215.             strcat(descrip_str, "t");
  1216.             else
  1217.             strcat(descrip_str, "f");
  1218.             break;
  1219.  
  1220.         case (mel_int):
  1221.  
  1222.             sprintf(temp_str, "%d", melo_datum.param[i].data.integer);
  1223.             strcat(descrip_str, temp_str);
  1224.             write_units_if_any(i);
  1225.             break;
  1226.  
  1227.         case (mel_real):
  1228.  
  1229.             sprintf(temp_str, "%lg", melo_datum.param[i].data.real);
  1230.             strcat(descrip_str, temp_str);
  1231.             write_units_if_any(i);
  1232.             break;
  1233.  
  1234.         case (mel_str):
  1235.  
  1236.             sprintf(temp_str, "'%s'", melo_datum.param[i].data.string);
  1237.             strcat(descrip_str, temp_str);
  1238.             write_units_if_any(i);
  1239.             break;
  1240.  
  1241.         case(mel_int_array):
  1242.  
  1243.             strcat(descrip_str, "{");
  1244.  
  1245.             for (j = 0; j < melo_datum.param[i].array_len; j++) {
  1246.             if (j != 0) strcat(descrip_str, " ");
  1247.             sprintf(temp_str, "%d",
  1248.                 melo_datum.param[i].data.integer_array[j]);
  1249.             strcat(descrip_str, temp_str);
  1250.             }
  1251.  
  1252.             strcat(descrip_str, "}");
  1253.             write_units_if_any(i);
  1254.             break;
  1255.  
  1256.         case(mel_real_array):
  1257.  
  1258.             strcat(descrip_str, "{");
  1259.  
  1260.             for (j = 0; j < melo_datum.param[i].array_len; j++) {
  1261.             if (j != 0) strcat(descrip_str, " ");
  1262.             sprintf(temp_str, "%lg",
  1263.                 melo_datum.param[i].data.real_array[j]);
  1264.             strcat(descrip_str, temp_str);
  1265.             }
  1266.  
  1267.             strcat(descrip_str, "}");
  1268.             write_units_if_any(i);
  1269.             break;
  1270.  
  1271.         case(mel_str_array):
  1272.  
  1273.             strcat(descrip_str, "{");
  1274.  
  1275.             for (j = 0; j < melo_datum.param[i].array_len; j++) {
  1276.             if (j != 0) strcat(descrip_str, " ");
  1277.             sprintf(temp_str, "'%s'",
  1278.                 melo_datum.param[i].data.string_array[j]);
  1279.             strcat(descrip_str, temp_str);
  1280.             }
  1281.  
  1282.             strcat(descrip_str, "}");
  1283.             write_units_if_any(i);
  1284.             break;
  1285.         }
  1286.     }
  1287.  
  1288.     strcat(descrip_str, ";");
  1289.     }
  1290. }
  1291.  
  1292. /*
  1293. ---------------------------------------------------------------------------
  1294. module 8.1.1
  1295.  
  1296. write the units associated with a parameter to the end of the descriptor
  1297. string descrip_str. don't bother if no units associated with this parameter.
  1298. ---------------------------------------------------------------------------
  1299. */
  1300. static void write_units_if_any(
  1301. int param_num)
  1302. {
  1303.     char temp_str[MELO_MAX_DESCRIP_STR_LEN+1];
  1304.  
  1305.     if (!strlen(melo_datum.param[param_num].units)) return;
  1306.  
  1307.     sprintf(temp_str, " (%s)", melo_datum.param[param_num].units);
  1308.     strcat(descrip_str, temp_str);
  1309. }
  1310.